package roles

import (
	"math/rand/v2"
	"time"

	"gitee.com/cjianwen/go/demo/socks_v2/raft_demo/server/consts"
	"gitee.com/cjianwen/go/demo/socks_v2/raft_demo/server/global"
	"gitee.com/cjianwen/go/demo/socks_v2/raft_demo/server/types"
	"gitee.com/cjianwen/go/logs"
	"gitee.com/cjianwen/go/utils"

	"gitee.com/cjianwen/go/service"
)

type iRole interface {
	Reset()
	Run() error
}

// 三种角色类型
const (
	roleFollower  = iota // 跟随者
	roleCandidate        // 候选人
	roleLeader           // 领导者
)

// 只能用 var ， 不能用 const
var roleNames = []string{"follower", "condidate", "leader"}

type sendDataInfo struct {
	dataIdx      uint32
	respCount    uint
	step         byte // 1 已发送 2 commit
	lastSendTime time.Time
}

type roleState struct {
	*service.Module
	currRole          byte      // 当前角色
	roles             [3]iRole  // 所有的角色
	Term              uint32    // 当前的任期
	tryVoteTerm       uint32    // 当前投票的任期(follower 使用)，尝试投票的任期
	respVoteCount     uint32    // 响应的投票数量
	myVoteCount       uint32    // 获得的投票数量
	LastRecvPingTime  time.Time // leader 最后的 ping 时间
	randPingTimeout   uint32    // 随机的 ping 超时时间
	prot              *protocol
	dataId            uint32
	appendRespCount   uint32    // append 操作的响应数
	commitRespCount   uint32    // commit 操作的响应数量
	leaderPingDataIdx uint32    // followr 学习的时候要用到
	isAppending       bool      // follower 是否正在接收数据
	appendingTime     time.Time // follower 开始 append 的时间
}

func (r *roleState) GetDataIdx() uint32 {
	r.dataId++
	return r.dataId*100 + uint32(global.ServerInfo.ServerId)
}

func (r *roleState) AddData(data string) uint32 {
	if r.IsLeader() { // 只有 leader 才能直接添加数据
		ei := &types.EntryItem{Id: r.GetDataIdx(), Term: r.Term, Data: types.EntryData{Data: data}}
		global.DataEntries.AppendEntries(ei)
		return ei.Id
	}
	return 0
}

func (r *roleState) addCandidateVote(isOk bool) {
	if isOk {
		r.myVoteCount++
	}
	r.respVoteCount++
}

func (r *roleState) randomPingTimeout() {
	r.randPingTimeout = uint32(rand.Float32()*consts.RandPintTimeout) + consts.PingTimeout
}

func (r *roleState) Init() error {
	r.roles[roleFollower] = &follower{roleState: r}
	r.roles[roleCandidate] = &condidate{roleState: r}
	r.roles[roleLeader] = &leader{roleState: r}

	r.LastRecvPingTime = time.Now()
	r.randomPingTimeout()

	logs.Printf("ping timeout is: %d", r.randPingTimeout)
	return nil
}

func (r *roleState) Run() error {
	err := r.roles[r.currRole].Run()
	time.Sleep(time.Millisecond) // 让CPU占用不那么高
	return err
}

func (r *roleState) IsLeader() bool {
	return r.currRole == roleLeader
}

func (r *roleState) GetTerm() uint32 {
	return r.Term
}

func (r *roleState) IsFollower() bool {
	return r.currRole == roleFollower
}

func (r *roleState) IsCandidate() bool {
	return r.currRole == roleCandidate
}

func (r *roleState) GetRoleName() string {
	return roleNames[r.currRole]
}

func (r *roleState) chgTo(role byte) {
	if r.currRole != role {
		logs.Printf("role chgTo: %s -> %s", roleNames[r.currRole], roleNames[role])
		r.currRole = role
		r.roles[role].Reset()
	}
}

func (r *roleState) becomeCandidate() {
	r.chgTo(roleCandidate)
}

func (r *roleState) becomeFollower() {
	r.chgTo(roleFollower)
}

func (r *roleState) becomeLeader() {
	r.chgTo(roleLeader)
}

func (r *roleState) IsPingTimeout() bool {
	ret := utils.TimePassed(&r.LastRecvPingTime, r.randPingTimeout)
	if ret {
		r.randomPingTimeout() // 重新随机
	}
	return ret
}

func init() {
	module := &service.Module{Name: "Role", ModuleId: consts.MID_Roles}
	role := &roleState{Module: module}

	role.prot = newProto(role)
	global.Role = role
	service.RegisterModule(role)
}
